What is an operating system?
운영체제(Operating System)는 사용자가 컴퓨터를 사용하기 위해 필요한 소프트웨어이다. 컴퓨터 하드웨어 바로 위에 설치되어 사용자 및 다른 소프트웨어와 하드웨어를 연결하는 소프트웨어 계층 즉, 중개자 역할을 해준다.
이 글은 운영체제 프로세스, 쓰레드와 관련된 개념, 용어를 기록해 두기 위해 작성하였다.
프로그램 & 프로세스
프로그램(Program) : 컴퓨터를 실행시키기 위한 일련의 순차적으로 작성된 명령어의 모음. 정적인 개념으로, 실행 파일을 의미
프로세스(Process) : 실행되고 있는 프로그램의 작업 단위, 프로그램 실행의 주체, 인스턴스. 프로그램의 명령어 및 데이터가 메모리에 적재되면 그것이 프로세스이다.
프로시저(Procedure) : 어떤 행동을 수행하기 위한 일련의 작업 순서를 의미. 루틴, 서브루틴, 함수와 같은 뜻으로 사용되며 하나의 프로시저는 특정 작업을 수행하기 위한 프로그램의 일부이다.
시스템 호출(System Calls) : 운영 체제의 커널이 제공하는 서비스에 대해, 응용 프로그램의 요청에 따라 커널에 접근하기 위한 인터페이스. 보통 C나 C++과 같은 고급 언어로 작성된 프로그램들은 직접 시스템 호출을 사용할 수 없기 때문에 고급 API를 통해 시스템 호출에 접근하게 하는 방법이다.
스풀(Spooling) : 주변장치의 느린 속도로 인하여 발생하는 대기시간을 줄이기 위해 디스크 공간의 일정 부분을 활용해서 데이터를 저장하는 기술
버퍼링(Buffering) : 버퍼(Buffer) 메모리 영역에 사용 예정 데이터를 임시로 저장하여 처리속도 차이를 완화하기 위한 기술
프로그램의 실행 구조
- 다음에 실행할 명령어를 주기억 장치로 부터 읽어들인다. (Fetch)
- 명령어를 디코드(해석) 한다. (Decode)
- 피연산자 Operand를 주기억 장치로 부터 읽어온다. (Operand)
- 명령어를 실행한다. (Execute)
프로세스 주소 공간
-
Stack
함수의 호출과 관계되는 지역 변수와 매개변수가 저장되는 영역이다. Stack 영역의 값은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다. 메모리의 높은 주소에서 낮은 주소의 방향으로 할당된다. 재귀 함수가 너무 깊게 호출되거나 함수가 지역변수를 너무 많이 가지고 있어 stack 영역을 초과하면 stack overflow 에러가 발생한다.
-
Heap
런타임에 크기가 결정되는 영역이다. 사용자에 의해 공간이 동적으로 할당 및 해제된다. 주로 참조형 데이터 (ex. 클래스) 등의 데이터가 할당된다. 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.
-
Static Data Segment
전역 변수나 Static 변수 등 프로그램이 사용할 수 있는 데이터를 저장하는 영역이다.
어떤 프로그램에 전역/static 변수를 참조하는 코드가 존재한다면, 이 프로그램은 컴파일 된 후에 data 영역을 참조하게 된다.
프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸한다.
단, 초기화 되지 않은 변수가 존재한다면, 이는 (그림에는 표현되지는 않았지만 BSS 영역에 저장된다.)
-
Code Segment
프로그램이 실행될 수 있도록 CPU가 해석 가능한 기계어 코드가 저장되어 있는 공간으로, 프로그램이 수정되면 안 되므로 ReadOnly 상태로 저장 되어있다.
프로세스 상태
New : 프로세스가 막 생성된 상태
Ready : 프로세스가 CPU에 실행되기 위해 대기하는 상태
Running : 프로세스에 포함된 명령어가 실행되고 있는 상태
Waiting : 프로세스가 특정 자원이나 이벤트를 기다리는 상태
Terminated : 프로세스가 실행을 완료한 상태
PCB(Process Control Block) : 운영체제가 프로세스를 제어하기 위해 정보를 저장해 놓는 곳으로, 프로세스의 상태 정보를 저장하는 구조체. 상태 관리와 Context Switching을 위해서 필요
문맥 전환(Context Switch) : CPU의 사용 권한을 한 프로세스에서 다른 프로세스에게 넘기는 과정
Dispatch : 준비 상태에서 대기하고 있는 프로세스 중 하나가 프로세서를 할당받아 실행 상태로 전이되는 과정
Timeout(Interrupt) : 주어진 타임 슬라이스 동안 수행 완료되지 않은 프로세스가 준비 상태로 돌아가는 것
Event Wait(running -> waiting) : I/O 입출력 발생 (CPU 사용 중 I/O 행위가 필요하며 대기 상태로 이동)
Wake-Up(waiting ->ready) : I/O 요청이 완료되면 다시 ready 상태로 전이
쓰레드
쓰레드(Thread) : 프로세스 내에서 실행되는 흐름의 단위 혹은 CPU 스케줄링의 기본 단위. 프로세스는 자신만의 고유 공간과 자원을 할당받아 사용하지만 쓰레드는 다른 쓰레드와 공간과 자원을 공유하면서 사용한다.
사용자 스레드(User Thread) : 커널의 지원/인식 없이 사용자 공간에서 실행되며 Pthread, Java 등의 라이브러리를 통해 제공된다. 커널의 관여없이 스레드 생성, 스케줄링, 관리를 수행한다. 스레드의 멈춤은 프로세스 자체의 blocking을 유발한다.
커널 스레드(Kernel Thread) : 커널이 스레드를 인식하고 프로세스와 유사하게 스케줄링한다. Windows, Linux, macOS 등 대부분의 운영체제가 지원하고 있다. 각 커널 스레드는 CPU 스케줄링을 통해 병렬적으로 실행된다.
동시성(Concurrency)
동기화(Synchronization) : 시스템을 동시에 작동시키기 위해 작업들 사이의 수행 시기를 조절하여 사건이 동시에 일어나거나, 일정한간격을 두고 일어날 수 있도록 한다. 스레드의 경우 멀티 스레드 환경에서 스레드들의 수행시점을 조절한다.
경쟁 상태(race condition) : 공유 자원에 대해 여러 개의 프로세스(또는 스레드)가 동시에 접근을 시도할 때 접근의 타이밍이나 순서 등이 결과값에 영향을 줄 수 있는 상태
임계 구역(Critical Section) : 프로세스 간의 공유 자원을 접근하는 데 있어서 문제가 발생하지 않도록 한 번에 하나의 프로세스만 이용하며 다른 프로세스들의 접근을 제한하는 영역
상호배제(Mutual Exclusion) : 임계 구역을 어느 시점에서 단지 한 개의 프로세스만이 사용할 수 있도록 하며, 다른 프로세스가 현재 사용 중인 임계 구역에 대하여 접근하려고 할 때 이를 금지하는 행위
한정된 대기(Bounded Waiting) : 다른 프로세스의 기아(Starvation)를 막기 위해 한번 임계 구역에 들어간 프로세스는 다음번 임계 구역 접근에 제한이 생겨야 한다. 프로세스의 임계 영역 진입은 유한한 시간 내에 허용되어야 함.
Spinlock : 특정한 자원을 획득(Lock) 또는 해제(Unlock)를 통해 공유 자원에 대한 접근 권한을 관리하는 방법
Mutex(MUTual EXclusion) : 획득(Lock) 또는 해제(Unlock) 상태가 있으며 스핀락과 같이 접근 권한을 획득할 때까지 Busy Waiting 상태에 머무르지 않고 Sleep 상태로 들어가며 Wakeup 되면 권한을 획득하는 방법. Locking 메커니즘으로 오직 하나의 스레드만이 동일 시점에 뮤텍스를 얻어 임계 구역(Critical Section)에 접근할 수 있다.
Semaphore : 하나 이상의 스레드가 공유자원에 접근, 표현형은 정수로 표현하며 Lock, Unlock가 아닌 값을 올리고 내리는 방식으로 사용한다. 특정 자원에 접근할 때 semWait이 먼저 호출되어 임계 구역에 들어갈 수 있는지 확인 후 조건에 만족한다면 semWait을 빠져나와 임계 구역에 들어가게 되고 이후 semSignal이 호출되어 임계 구역을 빠져나오게 된다.
- Counting Semaphore : 구조체로 사용되며, 0 ~N 사이의 세마포어 변수로 사용된다. 임계구역에 한 번에 여러 프로세스/스레드가 접근할 수 있게 하여 상호배제(Mutual Exclusion) 가 보장되지 않지만, 한정대기(Bounded Waiting)는 보장된다.
- Binary Semaphore : Int 변수로 사용되며, 0 과 1 두 가지 값만 가능하다. 0, 1을 사용해서 임계구역에 프로세스/쓰레드가 하나씩만 접근할 수 있게 해준다. 상호배제(Mutual Exclusion)가 보장되지만 프로세스/스레드가 임계구역에 머물 수 있는 시간은 정해져 있지 않기 때문에, 한정 대기(Bounded Waiting)는 보장되지 않는다. 따라서 기아 현상을 초래할 수 있다.
교착상태(Deadlock) : 동시에 실행되어 각각 wait상태로 다른 쪽에서 signal을 해주기 전까지 기다리게 되는 상태
스케줄링
스케줄링(Scheduling) : 프로세스가 생성되어 실행될때 필요한 시스템의 여러자원을 해당 프로세스에게 할당하는 작업으로, 적절히 분배하면 CPU의 활용을 극대화하고, 프로세스 처리율(시간 당 작업량)을 늘릴 수 있다.
First-come first-served(FCFS) : 먼저 실행한 애플리케이션을 먼저 배정. 스케줄링이 직관적이고, 지속적으로 유용한 프로세스를 수행하여 처리율이 높다. 소요시간이 짧거나 중요한 작업이더라도 늦게 기다렸다가 실행되어야 하는 불상사가 생긴다. 다른 계획 없이 대기 큐에 차례대로 받으므로 평균반환시간이 굉장히 길다.
Shortest job first(SJF) : 가장 빨리 끝날 수 있는 애플리케이션부터 배정. 항상 짧은 작업을 먼저 처리하여 평균 대기시간이 가장 짧다. 수행시간이 긴 작업은 짧은 작업에 밀려 기아가 발생, 실행 시간을 예측할 수 없어 실용적이지 못함. 짧은 작업이 먼저 실행되므로 공정하지 못한 정책으로 볼 수도 있다.
Round Robin scheduling(RR) : 처음 요청에 먼저 배정, 만약 일정 시간 내에 종료하지 않으면 운영체제가 강제로 실행을 중단시키고 Ready Queue로 돌려보낸다. 시간 할당량이 너무 크면 FCFS 스케줄링과 다를 바가 없고, 너무 작으면 문맥 교환에 잦아져 작업 수행 방해, 오버헤드가 크게 증가하기 때문에 Time Quantum을 어떻게 정해야 할지가 난감한 방법이다.
Priority Scheduling : 우선 순위가 가장 높은 프로세스를 우선적으로 배정하는 방법. 우선 순위가 낮은 프로세스의 기아 상태를 해결하려면 초기에는 우선 순위가 낮더라도 시간이 갈수록 우선순위 레벨을 올려주는 방식을 적용하면 된다.
Reference
OS 스핀락(Spinlock), 뮤텍스(Mutex), 세마포어(Semaphore)의 특징과 차이점
Mutex vs Lock / Counting Semaphore vs Binary Semaphore / Semaphore vs Mutex